package com.yoso.shop.application.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.github.pagehelper.PageInfo;
import com.yoso.api.info.log.OrderLogInfo;
import com.yoso.base.infrastructure.constants.ErrorMsg;
import com.yoso.base.infrastructure.exception.BaseException;
import com.yoso.base.infrastructure.factory.BeanFactory;
import com.yoso.base.interfaces.controller.BaseResponse;
import com.yoso.base.interfaces.vo.PageVo;
import com.yoso.base.service.BaseService;
import com.yoso.shop.application.fegin.LogFeignServer;
import com.yoso.shop.application.service.ICartService;
import com.yoso.shop.application.service.ICommodityService;
import com.yoso.shop.application.service.IOrderService;
import com.yoso.shop.application.service.IStockService;
import com.yoso.shop.domain.order.OrderEntity;
import com.yoso.shop.domain.order.OrderEntityExample;
import com.yoso.shop.infrastructure.constants.error.OrderErrorMsg;
import com.yoso.shop.infrastructure.constants.order.OrderStatusEnums;
import com.yoso.shop.infrastructure.constants.stock.StockConstants;
import com.yoso.shop.infrastructure.factory.ShopBeanFactory;
import com.yoso.shop.infrastructure.utils.RabbitMqUtils;
import com.yoso.shop.interfaces.vo.cart.CartVo;
import com.yoso.shop.interfaces.vo.commodity.CommodityVo;
import com.yoso.shop.interfaces.vo.order.OrderVo;
import com.yoso.shop.interfaces.vo.stock.StockVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestBody;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;


/**
 * @author shizejin
 * @date 2022/1/29
 * @description:
 */
@Service
@Slf4j
public class OrderServiceImpl extends BaseService<OrderEntity, OrderVo, OrderEntityExample> implements IOrderService {
    @Autowired
    private IStockService stockService;
    @Autowired
    private ICartService cartService;
    @Autowired
    private RabbitMqUtils mqUtils;
    @Autowired
    private LogFeignServer logFeignServer;
    @Autowired
    private ShopBeanFactory shopBeanFactory;

    @Autowired
    private ICommodityService commodityService;

    @Override
    public OrderEntity V2E(OrderVo data) {
        return JSON.parseObject(JSON.toJSONString(data), OrderEntity.class);
    }

    @Override
    public OrderVo E2V(OrderEntity data) {
        return JSON.parseObject(JSON.toJSONString(data), OrderVo.class);
    }

    @Override
    public OrderEntityExample getEntityExample(Map<String, String> paramMap) {
        String username = paramMap.get("username");
        if (StringUtils.isBlank(username)) {
            throw BaseException.of("用户名" + ErrorMsg.MUST_NOT_NULL);
        }
        OrderEntityExample example = new OrderEntityExample();
        example.createCriteria()
                .andBuyerIdEqualTo(username);
        return example;
    }

    /**
     * 增加订单
     */
    @Override
    @Transactional(rollbackFor = BaseException.class)
    public BaseResponse addOrder(List<String> cartIdList) {
        List<OrderVo> orderVoList = CollUtil.newArrayList();
        for (String id : cartIdList) {
            CartVo cartVo = cartService.selectByPrimaryKey(id);
            orderVoList.add(shopBeanFactory.buildOrderVo(cartVo));
        }
        for (OrderVo orderVo : orderVoList) {
//        判断库存
            boolean judgeStockFlag = judgeStock(ShopBeanFactory.buildCommodityVo(orderVo));
            if (!judgeStockFlag) {
                log.info(OrderErrorMsg.STOCK_UNDER);
                //        写入日志
                orderVo.setStatus(OrderStatusEnums.FAIL.toString());
                orderVo.setRemark(OrderErrorMsg.STOCK_UNDER);
                sendOrderLogMsg(orderVo);
                return BaseResponse.error(OrderErrorMsg.STOCK_UNDER);
            }

            log.info("减少库存");
            StockVo stockVo = ShopBeanFactory.buildStockVo(orderVo);
            stockService.reduceStock(stockVo, StockConstants.SOURCE_ADD_ORDER, StockConstants.STOCK_REDUCE);

            log.info("刷新购物车");
            cartService.flushCart(orderVo);

            log.info("增加订单");
            orderVo.setStatus(OrderStatusEnums.UNPAID.toString());
            this.insert(orderVo);
            //        写入日志
            sendOrderLogMsg(orderVo);
//        延时订单确认
            sendDelayOrderConfirmMsg(orderVo);
        }

        return BaseResponse.success();
    }

    /**
     * 取消订单
     */
    @Override
    @Transactional(rollbackFor = BaseException.class)
    public BaseResponse cancelOrder(String orderId) {
        OrderVo dbVo = this.selectByPrimaryKey(orderId);
//        当前订单状态
        String status = dbVo.getStatus();
        log.info("取消订单：{}", JSON.toJSONString(dbVo));
//        订单还未支付，直接取消
        if (StringUtils.equalsAny(OrderStatusEnums.UNPAID.toString(), status)) {
//            增加库存
            stockService.addStock(ShopBeanFactory.buildStockVo(dbVo), StockConstants.SOURCE_REDUCE_ORDER, StockConstants.STOCK_ADD);
//            修改状态
            dbVo.setStatus(OrderStatusEnums.CANCELED.toString());
            this.updateByPrimaryKeySelective(dbVo);
//            发送订单日志
            dbVo.setOrderTime(DateUtil.now());
            dbVo.setRemark(String.format("订单未支付：%s", OrderStatusEnums.CANCELED.toString()));
            this.sendOrderLogMsg(dbVo);
            return BaseResponse.success("订单取消成功！！！");
        }

        if (StringUtils.equalsAny(status, OrderStatusEnums.UNSENT.toString(), OrderStatusEnums.SENT.toString(), OrderStatusEnums.COMPLETE.toString())) {
            log.info("修改订单状态");
            dbVo.setStatus(OrderStatusEnums.AFTERMARKET.toString());
            this.updateByPrimaryKey(dbVo);

//          通知写入订单日志
            dbVo.setOrderTime(DateUtil.now());
            dbVo.setRemark(String.format("订单状态修改：%s->%s，修改时间：%s", status, OrderStatusEnums.AFTERMARKET.toString(), dbVo.getOrderTime()));
            sendOrderLogMsg(dbVo);
            return BaseResponse.success(String.format(" 当前订单%s(%s)待售后处理", dbVo.getCode(), status));
        }
        return BaseResponse.error(String.format("订单取消失败！！！当前订单状态:%s。原因:%s。", status, dbVo.getRemark()));
    }

    /**
     * 处理取消订单
     */
    @Override
    @Transactional(rollbackFor = BaseException.class)
    public BaseResponse handlerCancelOrder(OrderVo orderVo) {
        String cancelFlag = orderVo.getCancelFlag();
        if (StrUtil.equals("1", cancelFlag)) {
//            修改订单状态
            orderVo.setStatus(OrderStatusEnums.CANCELED.toString());
            this.updateByPrimaryKeySelective(orderVo);
//            增加库存
            stockService.addStock(ShopBeanFactory.buildStockVo(orderVo), StockConstants.SOURCE_REDUCE_ORDER, "通过审核!" + StockConstants.STOCK_ADD);
//            写入日志
            orderVo.setOrderTime(DateUtil.now());
            orderVo.setRemark("订单取消成功!!!");
            sendOrderLogMsg(orderVo);
            return BaseResponse.success("订单取消成功!!!");
        }
//            修改订单状态
        orderVo.setStatus(OrderStatusEnums.FAIL.toString());
        this.updateByPrimaryKeySelective(orderVo);
//            写入日志
        orderVo.setOrderTime(DateUtil.now());
        orderVo.setRemark("订单取消失败!!!");
        sendOrderLogMsg(orderVo);
        return BaseResponse.success("订单取消失败!!!");
    }

    /**
     * 修改订单信息
     */
    @Override
    @Transactional(rollbackFor = BaseException.class)
    public BaseResponse updateOrderInfo(OrderVo vo) {
        log.info("修改订单信息");
        int row = this.updateRecord(vo);
        if (row != 1) {
            return BaseResponse.result(row);
        }
//        通知更新日志
        vo.setOrderTime(DateUtil.now());
        vo.setRemark("订单信息修改");
        sendOrderLogMsg(vo);
        return BaseResponse.result(row);
    }

    /**
     * 订单支付确认
     *
     * @param orderId 订单id
     */
    @Override
    public void orderPayConfirm(String orderId) {
        OrderVo orderVo = this.selectByPrimaryKey(orderId);
        String status = orderVo.getStatus();
        if (!StringUtils.equals(OrderStatusEnums.UNPAID.toString(), status)) {
            return;
        }
//        订单未支付，取消订单
        this.cancelOrder(orderId);
    }

    /**
     * 获取当前用户订单信息
     *
     * @param username 用户名
     * @return 订单列表
     */
    @Override
    public List<OrderVo> getUserOrder(String username) {
        OrderEntityExample example = new OrderEntityExample();
        example.createCriteria()
                .andBuyerIdEqualTo(username);
        List<OrderVo> orderVos = this.selectByExample(example);

        return buildDetailInfoList(orderVos);
    }

    private List<OrderVo> buildDetailInfoList(Iterable<OrderVo> orderVos) {
        if (CollUtil.isEmpty(orderVos)) {
            return CollUtil.newArrayList();
        }
        ArrayList<OrderVo> result = CollUtil.newArrayList();
        Map<String, CommodityVo> code2CommodityMap = this.commodityService.buildCommodityCodeToSelfMap();
        for (OrderVo orderVo : orderVos) {
            String code = orderVo.getCommodityCode();
            orderVo.setCommodity(code2CommodityMap.get(code));
            result.add(orderVo);
        }
        return result;
    }


    /**
     * 判断库存
     */
    private boolean judgeStock(CommodityVo commodityVo) {
        StockVo stockVo = ShopBeanFactory.buildStockVo(commodityVo);
        StockVo hisStockVo = this.stockService.findByUniqueKey(stockVo);
        return commodityVo.getNumber() <= hisStockVo.getNumber();
    }

    private void sendOrderLogMsg(OrderVo orderVo) {
        log.info("通知写入订单日志{}", JSON.toJSONString(orderVo));
        mqUtils.flushOrderInfo(BeanFactory.convertBean(orderVo, OrderLogInfo.class));
    }

    private void sendDelayOrderConfirmMsg(OrderVo orderVo) {
        log.info("延时订单确认发起：{}", JSON.toJSONString(orderVo));
        mqUtils.sendDelayOrderConfirmMsg(this.convert(orderVo, OrderLogInfo.class));
    }

    @Override
    public PageInfo<OrderVo> pageConditionsList(@RequestBody PageVo pageVo) {
        PageInfo<OrderVo> pageInfo = super.pageConditionsList(pageVo);
        pageInfo.setList(this.buildDetailInfoList(pageInfo.getList()));
        return pageInfo;
    }
}
